package elitequant.performance;

import static elitequant.util.UtilFunc.formatDate;
import static elitequant.util.UtilFunc.formatMoney;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;

import elitequant.data.DataBoard;
import elitequant.gui.TearsheetWindow;
import elitequant.order.FillEvent;
import elitequant.position.PortfolioManager;
import elitequant.position.Position;
import elitequant.util.Series;

/**
 * Record equity, positions, and trades in accordance to pyfolio format First
 * date will be the first data start date
 * 
 */
public class PerformanceManager {

	private String[] symbols;

	private BigDecimal realizedPnl;
	private BigDecimal unrealizedPnl;

	private Series<Date, BigDecimal> equitys;
	private Map<Date, Map<String, BigDecimal>> positions;
	private Map<Date, Map<String, Object>> trades;

	private Date lastTime;

	public PerformanceManager(String[] symbols) {
		this.symbols = symbols;

		reset();
	}

	public synchronized void reset() {
		realizedPnl = new BigDecimal(0);
		unrealizedPnl = new BigDecimal(0);

		equitys = new Series<Date, BigDecimal>();
		positions = new HashMap<Date, Map<String, BigDecimal>>();
		trades = new HashMap<Date, Map<String, Object>>();
	}

	public synchronized void updatePerformance(Date currentTime, PortfolioManager portfolioManager,
	        DataBoard dataBoard) {
		if (lastTime == null) {
			equitys.put(currentTime, new BigDecimal(0));
			lastTime = currentTime;
			return;
		} else if (!currentTime.equals(lastTime)) { // on a new time/date, calculate the performances for the last date
			Date performanceTime = lastTime;
			BigDecimal equity = new BigDecimal(0);

			Map<String, BigDecimal> timePosition = positions.get(performanceTime);
			if (timePosition == null) {
				timePosition = new HashMap<String, BigDecimal>();
				positions.put(performanceTime, timePosition);
			}

			for (Entry<String, Position> entry : portfolioManager.getPositions().entrySet()) {
				String sym = entry.getKey();
				Position pos = entry.getValue();
				BigDecimal cash = dataBoard.getLastPrice(sym).multiply(BigDecimal.valueOf(pos.getSize()));
				equity = equity.add(cash);
				timePosition.put(sym, cash);
			}
			timePosition.put("cash", portfolioManager.getCash());

			equitys.put(performanceTime, equity.add(portfolioManager.getCash()));
			equitys.put(currentTime, new BigDecimal(0));
			lastTime = currentTime;
		}
	}

	public synchronized void onFill(FillEvent fillEvent) {
		Map<String, Object> trade = new HashMap<String, Object>() {
			{
				put("amount", fillEvent.fillSize);
				put("price", fillEvent.fillPrice);
				put("symbol", fillEvent.fullSymbol);
			}
		};
		trades.put(fillEvent.timestamp, trade);
	}

	/**
	 * When a new data date comes in, it calcuates performances for the previous day
	 * This leaves the last date not updated. So we call the update explicitly
	 * 
	 * @param currentTime
	 * @param portfolioManager
	 * @param dataBoard
	 */
	public synchronized void updateFinalPerformance(Date currentTime, PortfolioManager portfolioManager,
	        DataBoard dataBoard) {

		Date performanceTime = currentTime;
		BigDecimal equity = new BigDecimal(0);

		Map<String, BigDecimal> timePosition = positions.get(performanceTime);
		if (timePosition == null) {
			timePosition = new HashMap<String, BigDecimal>();
			positions.put(performanceTime, timePosition);
		}
		for (Entry<String, Position> entry : portfolioManager.getPositions().entrySet()) {
			String sym = entry.getKey();
			Position pos = entry.getValue();
			BigDecimal cash = dataBoard.getLastPrice(sym).multiply(BigDecimal.valueOf(pos.getSize()));
			equity = equity.add(cash);
			timePosition.put(sym, cash);
		}
		timePosition.put("cash", portfolioManager.getCash());

		equitys.put(performanceTime, equity.add(portfolioManager.getCash()));

	}

	public void createTearsheet() {
		TearSheet ts = new TearSheet(equitys, positions, trades);
		ts.createTearSheet();
	}

	public void saveResults(String outputDir) {
		File equityFile = new File(outputDir, "equity.csv");
		try (BufferedWriter Writer = new BufferedWriter(
		        new OutputStreamWriter(new FileOutputStream(equityFile), "UTF-8"))) {
		    List<Date> dates = equitys.getKeyList();
		    List<BigDecimal> moneys = equitys.getValueList();
			for (int i = 0; i < dates.size(); i++) {
				Writer.write(String.format("%s,%s", formatDate(dates.get(i)), formatMoney(moneys.get(i))));
				Writer.newLine();
			}
			;

		} catch (Exception e) {
			System.out.println(String.format("write local data error, %s", e.getMessage()));
		}

		File positionsFile = new File(outputDir, "positions.csv");
		try (BufferedWriter writer = new BufferedWriter(
		        new OutputStreamWriter(new FileOutputStream(positionsFile), "UTF-8"))) {

			// write header
			for (String symbol : symbols) {
				writer.write(String.format(",%s", symbol));
			}
			writer.write(",cach");
			writer.newLine();

			// write data
			for (Entry<Date, Map<String, BigDecimal>> entry : positions.entrySet()) {
				writer.write(String.format("%s", formatDate(entry.getKey())));

				Map<String, BigDecimal> cashs = entry.getValue();
				for (String symbol : symbols) {
					BigDecimal symbolCash = cashs.get(symbol);
					if (symbolCash == null) {
						symbolCash = new BigDecimal(0);
					}
					writer.write(String.format(",%s", formatMoney(symbolCash)));
				}

				BigDecimal cash = cashs.get("cash");
				if (cash == null) {
					cash = new BigDecimal(0);
				}
				writer.write(String.format(",%s", formatMoney(cash)));

				writer.newLine();
			}
			;

		} catch (Exception e) {
			System.out.println(String.format("write local data error, %s", e.getMessage()));
		}

		File tradesFile = new File(outputDir, "trades.csv");
		try (BufferedWriter writer = new BufferedWriter(
		        new OutputStreamWriter(new FileOutputStream(tradesFile), "UTF-8"))) {

			// write header
			writer.write(",amount,price,symbol");
			writer.newLine();

			// write data
			for (Entry<Date, Map<String, Object>> entry : trades.entrySet()) {
				writer.write(String.format("%s", formatDate(entry.getKey())));

				Map<String, Object> item = entry.getValue();
				writer.write(String.format(",%d", item.get("amount")));
				writer.write(String.format(",%s", formatMoney((BigDecimal) item.get("price"))));
				writer.write(String.format(",%s", item.get("symbol")));

				writer.newLine();
			}
			;

		} catch (Exception e) {
			System.out.println(String.format("write local data error, %s", e.getMessage()));
		}

	}
}
